/* axios 请求封装 */
import Vue from 'vue'
import axios from 'axios'
// import md5 from 'js-md5'
import store from '../store'
import mvvm from '../main'

// 取消请求方法
const cancelToken = axios.CancelToken
// 响应状态码
export const CTS = {
  SUCCESS: 0,
  ERROR_LOGIN: 401, // 登录错误
}

// 正在请求中的 url，该请求结束之前，不能再次请求
const onlyRequestURL = []
function removeOnlyRequestURL(url) {
  let i = onlyRequestURL.indexOf(url)
  if (i >= 0) onlyRequestURL.splice(i, 1)
}

const instance = axios.create({
  // baseURL 将自动加在 url 前面，除非 url 是一个绝对 URL。
  // baseURL: 'http://localhost:3000',
  baseURL: process.env.VUE_APP_BASEURL,
  // 表示跨域请求时是否需要使用凭证，设置为 true 可携带 cookie，设置后需要后台配置对应的响应头
  withCredentials: false,
  // 超过该时间没响应，则默认请求失败
  timeout: 20000,
  // 自定义请求头
  headers: {
    'Content-Type': 'application/json',
  },
})

// 添加请求拦截器
instance.interceptors.request.use(
  config => {
    config.headers['token'] = store.state.user.token

    // 在发送请求之前做些什么
    // const token = store.state.user.token
    // if (token) {
    //   if (config.file) {
    //     // 上传文件时不用headers携带token，要向 form 对象里添加token
    //     config.data.append('token', token)
    //   } else {
    //     Object.assign(config.headers, {
    //       'token': token,
    //     })
    //   }
    // }

    if (config.only) {
      config.cancelToken = new cancelToken(cancel => {
        // 执行 cancel，提前取消请求
        if (onlyRequestURL.includes(config.url)) cancel()
      })
      onlyRequestURL.push(config.url)
    }

    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// 添加响应拦截器
instance.interceptors.response.use(
  res => {
    if (res.data.code === CTS.ERROR_LOGIN) {
      mvvm.$store.dispatch('user/logout')
      mvvm.$router.push(`/login?redirect=${mvvm.$route.path}`)
    }
    return res
  },
  error => {
    return Promise.reject(error)
  }
)

/*
* get请求
* @param url[string]
* @param data[object]
* @param set[object]
*   timeout 设置响应超时时间
*   only 相同的api请求会取消上一次尚未完成的请求
* @returns {Promise}
*/
export function apiGet(url, params = {}, set = {}) {
  if (set.timeout === undefined) set.timeout = 20000 // 限制响应时间

  return new RequestPromise(
    new Promise((resolve, reject) => {
      instance({
        method: 'GET',
        url,
        params,
        ...set,
      }).then(res => {
        removeOnlyRequestURL(url)
        res.data.ok = res.data.code === CTS.SUCCESS
        resolve(res.data)
      }).catch(err => {
        removeOnlyRequestURL(url)
        reject(err)
      })
    })
  )
}


/*
* post请求
* @param url
* @param data
* @param set
*   timeout 设置响应超时时间
*   only 相同的api请求会取消上一次尚未完成的请求
* @returns Promise
*/
export function apiPost(url, data = {}, set = {}) {
  if (set.timeout === undefined) set.timeout = 20000 // 设置响应时间

  return new RequestPromise(
    new Promise((resolve, reject) => {
      instance({
        method: 'POST',
        url,
        data,
        ...set,
      }).then(res => {
        removeOnlyRequestURL(url)
        res.data.ok = res.data.code === CTS.SUCCESS
        resolve(res.data)
      }).catch(err => {
        removeOnlyRequestURL(url)
        reject(err)
      })
    })
  )
}

/*
* post请求, 上传文件
* @param url
* @param data 改属性的值为二进制文件流
* @param set
*   timeout 设置响应超时时间
*   only 在请求结束前是否禁止重复的请求
* @returns Promise
*/
export function apiFile(url, data = {}, set = {}) {
  if (set.timeout === undefined) set.timeout = 20000 // 设置响应时间

  let f = new FormData()
  for (var k in data)
    f.append(k, data[k])

  data = f

  return new Promise((resolve, reject) => {
    instance({
      method: 'POST',
      // 上传文件的请求头
      headers: { 'Content-Type': 'multipart/form-data' },
      url,
      data,
      file: true,
      ...set,
    }).then(res => {
      removeOnlyRequestURL(url)
      res.data.ok = res.data.code === CTS.SUCCESS
      resolve(res.data)
    }).catch(err => {
      removeOnlyRequestURL(url)
      reject(err)
    })
  })
}

// 如果 Promise 没有添加 catch 处理，则默认打印 err 在控制台上
function RequestPromise(originalPromise) {
  this._promise = originalPromise
  this._catchPromise = Promise.resolve().then(() => this._promise).catch(err => console.log('Error', err))

  const methods = ['then', 'catch', 'finally']

  for (let method of methods) {
    this[method] = function (...args) {
      this._promise = this._promise[method](...args)
      return this
    }
  }
}

Vue.CTS = Vue.prototype.CTS = CTS
Vue.apiGet = Vue.prototype.apiGet = apiGet
Vue.apiPost = Vue.prototype.apiPost = apiPost

